package top.vains.config.shiro;

import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import top.vains.entity.Permission;
import top.vains.entity.User;
import top.vains.exception.CustomerException;
import top.vains.service.IPermissionService;
import top.vains.service.IUserService;
import top.vains.util.JwtUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 自定义shiroRealm
 * 用于鉴权和登陆操作
 *
 * @author vains
 * @version 1.0
 * @since 2020/2/18 15:32
 **/
@Slf4j
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private IUserService userService;

    @Autowired
    private IPermissionService permissionService;

    public UserRealm() {
        // 使用自定义的Matcher验证接口
        this.setCredentialsMatcher(new JwtCredentialsMatcher());
    }

    /**
     * 必须重写此方法，不然shiro会报错
     *
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 鉴权
     *
     * @param principalCollection 返回指定realm仅作为collection的单个subject对象
     * @return 返回权限信息，由shiro去比对对应的权限信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        /*
            当调用subject.login()方法成功之后会自动获得该对象，如果没有认证过或认证失败会抛出异常
            返回应用程序内使用的主要对象，以唯一标识拥有账户
         */
        User principal = (User) principalCollection.getPrimaryPrincipal();
        List<Permission> strings = permissionService.selectPermissionsById(principal.getUserId());
        Set<String> permissions = new HashSet<>();
        for (Permission permission : strings) {
            permissions.add(permission.getAuthorization());
        }
        info.setStringPermissions(permissions);
        return info;
    }

    /**
     * 身份验证
     * @param authenticationToken 账号信息
     * @return 返回账号密码给shiro，由shiro去验证
     * @throws AuthenticationException 身份验证异常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
        /*
         * AuthenticationToken
         * jwtToken重写了AuthenticationToken接口，并创建一个token变量
         * 在filter中将token存入jwtToken的token变量中
         * 这里直接getToken即可获取前端传递的Token值
         */
        String token = ((JwtToken) authenticationToken).getToken();
        // 解析token
        Claims claims = JwtUtils.parseJwt(token);
        /*
         * 根据JwtUtils机密方法获取参数
         * 插叙数据库获取对象
         * 如果为空： 抛出异常
         * 校验失败会抛出异常：AuthenticationException
         */
        String number = claims.getSubject();
        String password = (String) claims.get("password");

        User user = userService.login(number, password);
        if (user == null) {
            throw new CustomerException("账号密码有误！");
        }
        return new SimpleAuthenticationInfo(user, token, "userRealm");
    }
}
